using System.Data;
using T.FAS.Runtime.Base.Entity;
using MySql.Data.MySqlClient;
using System.Data.SqlClient;
using System.Collections.Generic;
using Npgsql;
using NpgsqlTypes;
using System;
using T.FAS.Runtime.DataAccess.Common;

namespace T.FAS.Runtime.DataAccess.Engine
{
    public interface IDatabaseFunction
    {
        IDbDataParameter GetParameter();
        IDbDataParameter GetParameter(DataType dataType);
        IDbDataParameter GetParameter(DatabaseColumnType dataType);
    }

    public class SampleDBFactory
    {
        private static readonly Dictionary<DatabaseType, IDatabaseFunction> s_cache = new Dictionary<DatabaseType, IDatabaseFunction>
        {
            { DatabaseType.MySQL, new MySqlFunction() },
            { DatabaseType.SQLServer, new SqlServerFunction() },
            { DatabaseType.PostgreSQL, new PostgreSQLFunction() }
        };

        public static IDbDataParameter GetParameter(DatabaseType databaseType)
        {
            if (s_cache.ContainsKey(databaseType))
            {
                return s_cache[databaseType].GetParameter();
            }
            else
                return default;
        }

        public static IDbDataParameter GetParameter(DatabaseType databaseType, DataType dataType)
        {
            if (s_cache.ContainsKey(databaseType))
            {
                return s_cache[databaseType].GetParameter(dataType);
            }
            else
                return default;
        }

        public static IDbDataParameter GetParameter(DatabaseType databaseType, DatabaseColumnType databaseColumnType)
        {
            if (s_cache.ContainsKey(databaseType))
            {
                return s_cache[databaseType].GetParameter(databaseColumnType);
            }
            else
                return default;
        }
    }

    public class SqlServerFunction : IDatabaseFunction
    {
        public IDbDataParameter GetParameter()
        {
            return new SqlParameter();
        }

        public IDbDataParameter GetParameter(DataType dataType)
        {
            var par = GetParameter() as SqlParameter;
            switch (dataType)
            {
                case DataType.Binary:
                    par.SqlDbType = SqlDbType.VarBinary;
                    break;
                //不能改类型！会有线上问题
                case DataType.Boolean:
                    par.SqlDbType = SqlDbType.Int;
                    break;
                case DataType.Int:
                    par.SqlDbType = SqlDbType.Int;
                    break;
                case DataType.Long:
                    par.SqlDbType = SqlDbType.BigInt;
                    break;
                case DataType.Decimal:
                    par.SqlDbType = SqlDbType.Decimal;
                    break;
                case DataType.DateTime:
                    par.SqlDbType = SqlDbType.DateTime;
                    break;
                case DataType.Timestamp:
                    par.SqlDbType = SqlDbType.DateTime;
                    break;
                case DataType.Date:
                    par.SqlDbType = SqlDbType.Date;
                    break;
                case DataType.Time:
                    par.SqlDbType = SqlDbType.Time;
                    break;
                case DataType.String:
                    //if (EnvironmentSetting.MulitLanguage.IsEnableUnicode)
                    //    par.SqlDbType = SqlDbType.NVarChar;
                    //else
                    //    par.SqlDbType = SqlDbType.VarChar;
                    par.SqlDbType = SqlDbType.NVarChar;
                    break;
                case DataType.Record:
                case DataType.List:
                default:
                    throw new System.Exception(string.Format(CommonStringClassEngine.NotSupportedTypeCast,dataType));
            }
            return par;
        }

        public IDbDataParameter GetParameter(DatabaseColumnType dataType)
        {
            var par = GetParameter() as SqlParameter;
            switch (dataType)
            {
                case DatabaseColumnType.Bit:
                    par.SqlDbType = SqlDbType.TinyInt;
                    break;
                case DatabaseColumnType.TinyInt:
                    par.SqlDbType = SqlDbType.TinyInt;
                    break;
                case DatabaseColumnType.SmallInt:
                    par.SqlDbType = SqlDbType.SmallInt;
                    break;
                case DatabaseColumnType.Int:
                    par.SqlDbType = SqlDbType.Int;
                    break;
                case DatabaseColumnType.BigInt:
                    par.SqlDbType = SqlDbType.BigInt;
                    break;
                case DatabaseColumnType.Decimal:
                    par.SqlDbType = SqlDbType.Decimal;
                    break;
                case DatabaseColumnType.DateTime:
                    par.SqlDbType = SqlDbType.DateTime;
                    break;
                case DatabaseColumnType.Timestamp:
                    par.SqlDbType = SqlDbType.DateTime;
                    break;
                case DatabaseColumnType.Date:
                    par.SqlDbType = SqlDbType.Date;
                    break;
                case DatabaseColumnType.Time:
                    par.SqlDbType = SqlDbType.Time;
                    break;
                case DatabaseColumnType.Nchar:
                    par.SqlDbType = SqlDbType.NChar;
                    break;
                case DatabaseColumnType.Nvarchar:
                    par.SqlDbType = SqlDbType.NVarChar;
                    break;
                case DatabaseColumnType.NvarcharMax:
                    par.SqlDbType = SqlDbType.NText;
                    break;
                case DatabaseColumnType.Char:
                    par.SqlDbType = SqlDbType.Char;
                    break;
                case DatabaseColumnType.Varchar:
                    if (EnvironmentSetting.MulitLanguage.IsEnableUnicode)
                        par.SqlDbType = SqlDbType.NVarChar;
                    else
                        par.SqlDbType = SqlDbType.VarChar;
                    break;
                case DatabaseColumnType.VarcharMax:
                    if (EnvironmentSetting.MulitLanguage.IsEnableUnicode)
                        par.SqlDbType = SqlDbType.NText;
                    else
                        par.SqlDbType = SqlDbType.Text;
                    break;
                case DatabaseColumnType.Binary:
                    par.SqlDbType = SqlDbType.Binary;
                    break;
                case DatabaseColumnType.Varbinary:
                    par.SqlDbType = SqlDbType.VarBinary;
                    break;
                case DatabaseColumnType.VarbinaryMax:
                    par.SqlDbType = SqlDbType.VarBinary;
                    break;
                default:
                    break;
            }

            return par;
        }
    }

    public class MySqlFunction : IDatabaseFunction
    {
        public IDbDataParameter GetParameter()
        {
            return new MySqlParameter();
        }

        public IDbDataParameter GetParameter(DataType dataType)
        {
            var par = GetParameter() as MySqlParameter;
            switch (dataType)
            {
                case DataType.Binary:
                    par.MySqlDbType = MySqlDbType.VarBinary;
                    break;
                case DataType.Boolean:
                    par.MySqlDbType = MySqlDbType.Bit;
                    break;
                case DataType.Int:
                    par.MySqlDbType = MySqlDbType.Int32;
                    break;
                case DataType.Long:
                    par.MySqlDbType = MySqlDbType.Int64;
                    break;
                case DataType.Decimal:
                    par.MySqlDbType = MySqlDbType.Decimal;
                    break;
                case DataType.DateTime:
                    par.MySqlDbType = MySqlDbType.DateTime;
                    break;
                case DataType.Timestamp:
                    par.MySqlDbType = MySqlDbType.DateTime;
                    break;
                case DataType.Date:
                    par.MySqlDbType = MySqlDbType.Date;
                    break;
                case DataType.Time:
                    par.MySqlDbType = MySqlDbType.Time;
                    break;
                case DataType.String:
                    par.MySqlDbType = MySqlDbType.VarChar;
                    break;
                case DataType.Record:
                case DataType.List:
                default:
                    throw new System.Exception(string.Format(CommonStringClassEngine.NotSupportedTypeCast, dataType));
            }
            return par;
        }

        public IDbDataParameter GetParameter(DatabaseColumnType dataType)
        {
            var par = GetParameter() as MySqlParameter;
            switch (dataType)
            {
                case DatabaseColumnType.Bit:
                    par.MySqlDbType = MySqlDbType.Int16;
                    break;
                case DatabaseColumnType.TinyInt:
                    par.MySqlDbType = MySqlDbType.Int16;
                    break;
                case DatabaseColumnType.SmallInt:
                    par.MySqlDbType = MySqlDbType.Int16;
                    break;
                case DatabaseColumnType.Int:
                    par.MySqlDbType = MySqlDbType.Int32;
                    break;
                case DatabaseColumnType.BigInt:
                    par.MySqlDbType = MySqlDbType.Int64;
                    break;
                case DatabaseColumnType.Decimal:
                    par.MySqlDbType = MySqlDbType.Decimal;
                    break;
                case DatabaseColumnType.DateTime:
                    par.MySqlDbType = MySqlDbType.DateTime;
                    break;
                case DatabaseColumnType.Timestamp:
                    par.MySqlDbType = MySqlDbType.DateTime;
                    break;
                case DatabaseColumnType.Date:
                    par.MySqlDbType = MySqlDbType.Date;
                    break;
                case DatabaseColumnType.Time:
                    par.MySqlDbType = MySqlDbType.Time;
                    break;
                case DatabaseColumnType.Nchar:
                    par.MySqlDbType = MySqlDbType.VarChar;
                    break;
                case DatabaseColumnType.Nvarchar:
                    par.MySqlDbType = MySqlDbType.VarChar;
                    break;
                case DatabaseColumnType.NvarcharMax:
                    par.MySqlDbType = MySqlDbType.Text;
                    break;
                case DatabaseColumnType.Char:
                    par.MySqlDbType = MySqlDbType.VarChar;
                    break;
                case DatabaseColumnType.Varchar:
                    par.MySqlDbType = MySqlDbType.VarChar;
                    break;
                case DatabaseColumnType.VarcharMax:
                    par.MySqlDbType = MySqlDbType.Text;
                    break;
                case DatabaseColumnType.Binary:
                    par.MySqlDbType = MySqlDbType.Binary;
                    break;
                case DatabaseColumnType.Varbinary:
                    par.MySqlDbType = MySqlDbType.VarBinary;
                    break;
                case DatabaseColumnType.VarbinaryMax:
                    par.MySqlDbType = MySqlDbType.VarBinary;
                    break;
                default:
                    throw new System.Exception(string.Format(CommonStringClassEngine.NotSupportedTypeCast, dataType));
            }

            return par;
        }
    }

    public class PostgreSQLFunction : IDatabaseFunction
    {
        public IDbDataParameter GetParameter()
        {
            return new NpgsqlParameter();
        }

        public IDbDataParameter GetParameter(DataType dataType)
        {
            var par = GetParameter() as NpgsqlParameter;
            switch (dataType)
            {
                case DataType.Binary:
                    par.NpgsqlDbType = NpgsqlDbType.Bytea;
                    break;
                case DataType.Boolean:
                    par.NpgsqlDbType = NpgsqlDbType.Bit;
                    break;
                case DataType.Int:
                    par.NpgsqlDbType = NpgsqlDbType.Integer;
                    break;
                case DataType.Long:
                    par.NpgsqlDbType = NpgsqlDbType.Bigint;
                    break;
                case DataType.Decimal:
                    par.NpgsqlDbType = NpgsqlDbType.Numeric;
                    break;
                case DataType.DateTime:
                    par.NpgsqlDbType = NpgsqlDbType.Timestamp;
                    break;
                case DataType.Timestamp:
                    par.NpgsqlDbType = NpgsqlDbType.Timestamp;
                    break;
                case DataType.Date:
                    par.NpgsqlDbType = NpgsqlDbType.Date;
                    break;
                case DataType.Time:
                    par.NpgsqlDbType = NpgsqlDbType.Time;
                    break;
                case DataType.String:
                    par.NpgsqlDbType = NpgsqlDbType.Varchar;
                    break;
                case DataType.Record:
                case DataType.List:
                default:
                    throw new Exception($"Not Supported PostgreSQL DataType:{dataType}");
            }
            return par;
        }

        public IDbDataParameter GetParameter(DatabaseColumnType dataType)
        {
            var par = GetParameter() as NpgsqlParameter;
            switch (dataType)
            {
                case DatabaseColumnType.Bit:
                    par.NpgsqlDbType = NpgsqlDbType.Bit;
                    break;
                case DatabaseColumnType.TinyInt:
                    par.NpgsqlDbType = NpgsqlDbType.Smallint;
                    break;
                case DatabaseColumnType.SmallInt:
                    par.NpgsqlDbType = NpgsqlDbType.Smallint;
                    break;
                case DatabaseColumnType.Int:
                    par.NpgsqlDbType = NpgsqlDbType.Integer;
                    break;
                case DatabaseColumnType.BigInt:
                    par.NpgsqlDbType = NpgsqlDbType.Bigint;
                    break;
                case DatabaseColumnType.Decimal:
                    par.NpgsqlDbType = NpgsqlDbType.Numeric;
                    break;
                case DatabaseColumnType.DateTime:
                    par.NpgsqlDbType = NpgsqlDbType.Timestamp;
                    break;
                case DatabaseColumnType.Timestamp:
                    par.NpgsqlDbType = NpgsqlDbType.Timestamp;
                    break;
                case DatabaseColumnType.Date:
                    par.NpgsqlDbType = NpgsqlDbType.Date;
                    break;
                case DatabaseColumnType.Time:
                    par.NpgsqlDbType = NpgsqlDbType.Time;
                    break;
                case DatabaseColumnType.Nchar:
                    par.NpgsqlDbType = NpgsqlDbType.Varchar;
                    break;
                case DatabaseColumnType.Nvarchar:
                    par.NpgsqlDbType = NpgsqlDbType.Varchar;
                    break;
                case DatabaseColumnType.NvarcharMax:
                    par.NpgsqlDbType = NpgsqlDbType.Text;
                    break;
                case DatabaseColumnType.Char:
                    par.NpgsqlDbType = NpgsqlDbType.Char;
                    break;
                case DatabaseColumnType.Varchar:
                    par.NpgsqlDbType = NpgsqlDbType.Varchar;
                    break;
                case DatabaseColumnType.VarcharMax:
                    par.NpgsqlDbType = NpgsqlDbType.Text;
                    break;
                case DatabaseColumnType.Binary:
                    par.NpgsqlDbType = NpgsqlDbType.Bytea;
                    break;
                case DatabaseColumnType.Varbinary:
                    par.NpgsqlDbType = NpgsqlDbType.Bytea;
                    break;
                case DatabaseColumnType.VarbinaryMax:
                    par.NpgsqlDbType = NpgsqlDbType.Bytea;
                    break;
                default:
                    throw new Exception(string.Format(CommonStringClassEngine.NotSupportedTypeCast, dataType));
            }

            return par;
        }
    }
}
